
import types
import traceback
import wx
import wx.lib.sized_controls as sc


def _extractErrorInfo(exc_info):
    # analyze exc_info first to decide the layout
    tp, val = exc_info[:2]
    # traceback list entries: (filename, line number, function name, text)
    tb = traceback.extract_tb(exc_info[2])
    # --Based on traceback.py::format_exception_only()--
    if isinstance(tp, types.ClassType):
        etp = tp.__name__
    else:
        etp = tp

    if tp is SyntaxError:
        try:
            msg, (filename, lineno, _, line) = val
        except ValueError:
            pass
        else:
            if not filename:
                filename = "<string>"
            line = line.strip()
            tb.append((filename, lineno, '', line))
            val = msg

    try:
        edt = str(val)
    except (TypeError, ValueError):
        edt = "<unprintable %s object>" & type(val).__name__

    return etp, edt, tb


def printExcInfo(exc_info):
    etp, edt, tb = _extractErrorInfo(exc_info)
    ret = 'sys.exc_info: Type: %s\nDetails:%s\n' % (str(etp), edt)
    if len(tb) > 0:
        ret += 'Error:\n'
        for t in tb:
            ret += ' File: %s, Line: %d, Func: %s, \
                    Code: %s\n' % (t[0], t[1], t[2], t[3])

    return ret


class ErrorDialog(sc.SizedDialog):
    def __init__(self, parent, exc_info):
        sc.SizedDialog.__init__(self, parent, -1, "Errors",
                                style=wx.DEFAULT_DIALOG_STYLE |
                                wx.RESIZE_BORDER)

        sizer = wx.BoxSizer(wx.VERTICAL)
        etp, edt, tb = _extractErrorInfo(exc_info)
        # first row
        self.listCtrl = wx.ListCtrl(self, -1, size=(550, 120),
                                    style=wx.LC_REPORT)
        # self.listCtrl.SetSizerProps(expand=True, proportion=1)
        self.configureListCtrl(tb)
        sizer.Add(self.listCtrl, 1, wx.EXPAND | wx.ALL, 5)

        # second row
        self.lblDetails = wx.StaticText(self, -1, "Error Details")
        sizer.Add(self.lblDetails, 0, wx.EXPAND | wx.ALL, 5)

        # third row
        self.details = wx.TextCtrl(self, -1,
                                   style=wx.TE_MULTILINE | wx.TE_READONLY)
        # self.details.SetSizerProps(expand=True, proportion=1)
        details = 'Type: %s\nDetails:%s' % (str(etp), edt)
        self.details.SetValue(details)
        sizer.Add(self.details, 1, wx.EXPAND | wx.ALL, 5)

        if wx.Platform == '__WXGTK__':
            wx.StaticLine(self, -1, size=(20, -1), style=wx.LI_HORIZONTAL)
            sizer.Add(wx.Button(self, wx.ID_OK, "OK"),
                      0, wx.ALIGN_CENTER | wx.ALL, 5)

        self.SetSizer(sizer)
        self.Fit()
        self.SetMinSize(self.GetSize())

    def configureListCtrl(self, info):
        # set list header
        self.listCtrl.InsertColumn(0, "Filename")
        self.listCtrl.InsertColumn(1, "Line")
        self.listCtrl.InsertColumn(2, "Code")
        self.listCtrl.SetColumnWidth(0, 200)
        self.listCtrl.SetColumnWidth(1, 50)
        self.listCtrl.SetColumnWidth(2, 300)
        # anaylze the info
        for k, v in enumerate(info):
            (filename, lineno, _, code) = v
            self.listCtrl.InsertStringItem(k, filename)
            self.listCtrl.SetStringItem(k, 1, str(lineno))
            # for Executable, the code is None
            if code:
                self.listCtrl.SetStringItem(k, 2, code)
